﻿#define SLEEPMODE
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.OleDb;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.IO.Ports;
using System.Text.RegularExpressions;
using System.Threading;
using System.Runtime.InteropServices;
using System.Resources;
using System.Media;
using System.Runtime.Serialization.Formatters.Binary;
//using MonitorPanel;


namespace FIMonitor
{
    public partial class FIMForm : Form
    {
        [DllImport("user32.dll")]　
        static extern Boolean LockWindowUpdate(IntPtr hWndLock);

        public string strFilePath = "";
        private string strComPort = "";
        private IniFiles cfg;
        public Thread _sthread = null;
        private Thread _qthread = null;
        private Thread _pthread = null;
        private Thread _savethread = null;

        public SerialPort comm = null;
        public LoginFrm login_frm = null;

        private bool bVoiceWarning = true;

        public bool bAdminLogin = false;
        public String strLoginUsername = "";

        private const int WAITTIMEOUT = 1000; //轮询超时时间ms
        private const int MAXQUEUECOUNT = 1024;
        public static object tempLock = new object();      //收发线程对以上四个共享资源访问的互斥锁
        private static Object commLock = new Object();

        private static Queue<byte> qdata_recv = new Queue<byte>(MAXQUEUECOUNT); 

        private bool bComRecving = false;
        private bool bComSending = false;
        private bool bComClosing = false;

        public Hashtable soundFiles;

        private StringBuilder strBuilder = new StringBuilder();
        private StringBuilder strLogBuf = new StringBuilder();
        private StringBuilder strLogQueue = new StringBuilder();
        private StringBuilder strLogRcvData = new StringBuilder();
        public StringBuilder strLogCount = new StringBuilder();

        private bool bneedtoupdatelistview = false;

        public bool bNeedtoUpdateListView
        {
            get
            {
                return bneedtoupdatelistview;
            }
            set
            {
                if (value)
                {
                    bneedtoupdatelistview = value;
                    this.WhenFIMStatusChanged();
                }
                else bneedtoupdatelistview = false;
            }
        }

        private AutoResetEvent recvEvent = new AutoResetEvent(false);   //轮询时收发同步事件
        private AutoResetEvent sendEvent = new AutoResetEvent(false);

        public delegate void StatusChanged(object sender, EventArgs e);
        public event StatusChanged OnFIMStatusChanged;
        private delegate void FIMStatusDelegate();

        public delegate void monitorDataChangedEventHandler(object sender, MonitorArgs e);
        public event monitorDataChangedEventHandler OnMonitorDataChanged;

        public int[] addr_id = new int[256];       //监护仪的addr和id的对应数组，索引为addr

        public string ComPort
        {
            get
            {
                return strComPort;
            }
            set
            {
                strComPort = value;
            }
        }

        public FIMForm()
        {
            strFilePath = System.Windows.Forms.Application.StartupPath;
            InitializeComponent();

            soundFiles = new Hashtable();

            soundFiles.Add("FINISH", "");
            soundFiles.Add("LOWBATTERY", "");
            soundFiles.Add("DISCONNECT", "");
            soundFiles.Add("DIDOU", "");
            soundFiles.Add("TIMEREMIND", "");
            soundFiles.Add("VALUEREMIND", "");
            soundFiles.Add("BLOCK", "");
            soundFiles.Add("FLUIDDEFICIENT", "");

            for (int i = 0; i < addr_id.Length; ++i)
            {
                addr_id[i] = -1;
            }
        }

        private void FIMForm_Load(object sender, EventArgs e)
        {
            this.bAdminLogin = login_frm.bAdmin;
            this.strLoginUsername = login_frm.strLoginUsername;
            this.StatusUserLabel.Text = this.strLoginUsername;

            if (!bAdminLogin)
            {
                this.用户管理ToolStripMenuItem.Text = "修改密码";
            }

            this.开启报警ToolStripMenuItem.Text = "关闭";
            
            monitorPanel1.parentForm = this;
            MonitorPanel.MonitorPanel.strFilePath = this.strFilePath;
            OnFIMStatusChanged += new StatusChanged(UpdateListView);

            this.splitContainer2.SplitterDistance = this.splitContainer2.Height; 

            int width = WarningListView.Width;
            WarningListView.Columns.Add("RoomID", "房间号");
            int w1 = WarningListView.Columns["RoomID"].Width = (int)(0.25 * width);
            
            WarningListView.Columns.Add("BedID", "床位号");
            int w2 = WarningListView.Columns["BedID"].Width = (int)(0.25 * width);

            WarningListView.Columns.Add("WarningMessage", "告警信息");
            WarningListView.Columns["WarningMessage"].Width = (int)(width - w1 - w2 - 4);

            WarningListView.FullRowSelect = true;

            //this.调试模式ToolStripMenuItem.Visible = false;

            Size panelSize = new Size(this.splitContainer1.Panel1.ClientSize.Width, (int)(this.splitContainer1.Panel1.ClientSize.Height * 1.6));
            //monitorPanel1.SetSize(this.splitContainer1.Panel1.ClientSize);
            monitorPanel1.SetSize(panelSize);
            monitorPanel1.DrawMonitor();

            //this.monitorPanel1.LostFocus += new EventHandler(monitorPanel1_LostFocus);
            //this.monitorPanel1.GotFocus += new EventHandler(monitorPanel1_GotFocus);

            ConfigInit(strFilePath);
            OpenComm();

            //this.LoadMonitorFromMdb();
            //this.LoadBedIDFromMdb();

            _sthread = new Thread(new ThreadStart(GetMonitorStatusThread));
            _sthread.Start();

#if !SLEEPMODE
            _qthread = new Thread(new ThreadStart(GetDataFromQueue));
            _qthread.Start();
#endif

            _savethread = new Thread(new ThreadStart(SaveStatusToMdb));
            _savethread.Start();

            _pthread = new Thread(new ThreadStart(PlayWarningSoundCycleThread));
            _pthread.Start();
        }
        /*
        private Point lastPosition_Autoscroll = new Point(0, 0);
        
        private void monitorPanel1_GotFocus(object sender, EventArgs e)
        {
            Point newScrollPosition = new Point(0 - lastPosition_Autoscroll.X, 0 - lastPosition_Autoscroll.Y);            
            this.splitContainer1.Panel1.AutoScrollPosition = newScrollPosition;
        }

        private void monitorPanel1_LostFocus(object sender, EventArgs e)
        {            
            lastPosition_Autoscroll = this.splitContainer1.Panel1.AutoScrollPosition;            
        }
        */
        public void SetSplitContainerFocus()
        {
            this.splitContainer1.Panel1.Focus();
        }

        public OleDbConnection ConnInit()
        {
            string strMdbFile = strFilePath + "\\FIMonitor.mdb";
            string strConn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + strMdbFile + ";Jet OLEDB:Database Password=112358;Persist Security Info=True";

            try
            {
                OleDbConnection conn = new OleDbConnection(strConn);
                conn.Open();
                return conn;
            }
            catch (System.Exception ex)
            {
                MessageBox.Show(ex.Message.ToString());
                return null;
            }
        }

        private void ConfigInit(string _strFilePath)
        {
            string strCfgFile = _strFilePath + "\\Config.ini";

            cfg = new IniFiles(strCfgFile);

            strComPort = cfg.ReadString("ComPort", "Port", "");

            string str = cfg.ReadString("SOUNDFILES", "FINISH", "");
            soundFiles["FINISH"] = str.Substring(0, str.LastIndexOf('v') + 1);

            str = cfg.ReadString("SOUNDFILES", "LOWBATTERY", "");
            soundFiles["LOWBATTERY"] = str.Substring(0, str.LastIndexOf('v') + 1);

            str = cfg.ReadString("SOUNDFILES", "DISCONNECT", "");
            soundFiles["DISCONNECT"] = str.Substring(0, str.LastIndexOf('v') + 1);

            str = cfg.ReadString("SOUNDFILES", "DIDOU", "");
            soundFiles["DIDOU"] = str.Substring(0, str.LastIndexOf('v') + 1);

            str = cfg.ReadString("SOUNDFILES", "VALUEREMIND", "");
            soundFiles["VALUEREMIND"] = str.Substring(0, str.LastIndexOf('v') + 1);

            str = cfg.ReadString("SOUNDFILES", "BLOCK", "");
            soundFiles["BLOCK"] = str.Substring(0, str.LastIndexOf('v') + 1);

            str = cfg.ReadString("SOUNDFILES", "FLUIDDEFICIENT", "");
            soundFiles["FLUIDDEFICIENT"] = str.Substring(0, str.LastIndexOf('v') + 1);
        }

        private void 系统设置ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SysCfgFrm sysfrm = new SysCfgFrm(this);
            if (sysfrm.ShowDialog() == DialogResult.OK)
            {
                cfg.WriteString("ComPort", "Port", strComPort);
            }
        }

        private void SerialInit()
        {
            comm = new SerialPort();
            comm.PortName = strComPort;
            comm.BaudRate = 9600;
            comm.Parity = Parity.None;
            comm.DataBits = 8;
            comm.StopBits = StopBits.One;

            this.StatusSerialPortLabel.Text = this.comm.PortName;

#if SLEEPMODE
            comm.ReceivedBytesThreshold = 1;
#else
            comm.ReceivedBytesThreshold = 34;
#endif
            comm.DataReceived += new SerialDataReceivedEventHandler(DataReceived);
        }

        //加载RoomID和BedID并显示，已修改在MonitorPanel中实现
        public void LoadBedIDFromMdb()
        {
            OleDbConnection conn = null;
            DataTable dt = new DataTable();
            try
            {
                conn = this.ConnInit();
                string sql = @"SELECT MonitorID, RoomID, BedID FROM [BedID]";
                OleDbCommand cmd = new OleDbCommand(sql, conn);
                OleDbDataReader rdr = cmd.ExecuteReader();
                dt.Load(rdr);

                for (int i = 0; i < dt.Rows.Count; ++i)
                {
                    int monitorid = int.Parse(dt.Rows[i]["MonitorID"].ToString());
                    MonitorPanel.Monitor monitor = monitorPanel1.Monitors[monitorid];

                    monitor.RoomID = int.Parse(dt.Rows[i]["RoomID"].ToString());
                    monitor.BedID = int.Parse(dt.Rows[i]["BedID"].ToString());

                    monitor.bDrawAllOneTime = false;
                    monitor.bNeedtoConfig = false;
                    //monitor.drawMonitor();
                    //monitorPanel1.DrawPanel.Invalidate(monitor.out_rec);
                }
                rdr.Close();
                dt.Dispose();
                monitorPanel1.DrawMonitor();
            }
            catch (System.Exception ex)
            {
            	
            }
            finally
            {
                if (conn != null)
                    conn.Close();
            }
        }

        public void LoadMonitorFromMdb()
        {
            if (comm == null || !comm.IsOpen)
            {
                //return;
            }

            for (int i = 0; i < monitorPanel1.Monitors.Count; ++i)
            {
                MonitorPanel.Monitor monitor = monitorPanel1.Monitors[i];

                if (monitor.STATUS != MonitorPanel.Monitor.MSTATUS.UNMONITOR)
                {
                    this.addr_id[monitor.MonitorAddr] = -1;
                    monitor.STATUS = MonitorPanel.Monitor.MSTATUS.UNMONITOR;
                    monitor.bNeedtoConfig = false;

                    monitor.RoomID = 0;
                    monitor.BedID = 0;
                    monitor.Patientname = "uname";
                    monitor.MonitorAddr = 0;
                    monitor.Sex = MonitorPanel.Monitor.SEX.MALE;
                    monitor.Age = 0;
                    monitor.PipeType = 0;
                    monitor.RemindTime = 0;
                    monitor.RemindValue = 0;
                    monitor.TotalCP = 0;
                    monitor.TotalTime = 0;
                }
            }

            OleDbConnection conn = null;
            DataTable dt = new DataTable();

            try
            {
                conn = this.ConnInit();
                string sql = @"SELECT ID,Name,Sex,Age,RoomID,BedID,MonitorAddr,PipeType,Speed,ValueRemind,TimeRemind,TotalCP,TotalTime FROM [Patient] WHERE Status=TRUE";

                OleDbCommand cmd = new OleDbCommand(sql, conn);
                OleDbDataReader rdr = cmd.ExecuteReader();
                dt.Load(rdr);

                for (int i = 0; i < dt.Rows.Count; ++i)
                {
                    MonitorPanel.Monitor monitor = monitorPanel1.Monitors[i];

                    monitor.bDrawAllOneTime = true;
                    monitor.PatientID = int.Parse(dt.Rows[i]["ID"].ToString());
                    monitor.RoomID = int.Parse(dt.Rows[i]["RoomID"].ToString());
                    monitor.BedID = int.Parse(dt.Rows[i]["BedID"].ToString());
                    monitor.Patientname = dt.Rows[i]["Name"].ToString();
                    monitor.Age = int.Parse(dt.Rows[i]["Age"].ToString());

                    if (dt.Rows[i]["Sex"].ToString() == "男")
                    {
                        monitor.Sex = MonitorPanel.Monitor.SEX.MALE;
                    }
                    else monitor.Sex = MonitorPanel.Monitor.SEX.FEMALE;

                    monitor.MonitorAddr = byte.Parse(dt.Rows[i]["MonitorAddr"].ToString());
                    monitor.PipeType = byte.Parse(dt.Rows[i]["PipeType"].ToString());
                    monitor.Speed = byte.Parse(dt.Rows[i]["Speed"].ToString());
                    monitor.RemindValue = Int16.Parse(dt.Rows[i]["ValueRemind"].ToString());
                    monitor.RemindTime = Int16.Parse(dt.Rows[i]["TimeRemind"].ToString());
                    monitor.TotalCP = Int16.Parse(dt.Rows[i]["TotalCP"].ToString());
                    monitor.TotalTime = Int16.Parse(dt.Rows[i]["TotalTime"].ToString());

                    this.addr_id[monitor.MonitorAddr] = monitor.MonitorID;
                    monitor.STATUS = MonitorPanel.Monitor.MSTATUS.MONITORING;

                    monitor.bDrawAllOneTime = false;
                    monitor.bNeedtoConfig = false;
                    //monitor.drawMonitor();
                    //monitorPanel1.DrawPanel.Invalidate(monitor.out_rec);
                }
                rdr.Close();
                dt.Dispose();
            }
            catch (System.Exception ex)
            {
                MessageBox.Show(ex.Message.ToString());
                return;
            }
            finally
            {
                if (conn != null)
                {
                    conn.Close();
                }
            }
        }

        //每隔一段时间自动保存实时数据到CurrentInfo表中
        private void SaveStatusToMdb()
        {
            while (true)
            {
                Thread.Sleep(10000);
                foreach (MonitorPanel.Monitor monitor in monitorPanel1.Monitors)
                {
                    Thread.Sleep(10000);

                    if (comm == null || !comm.IsOpen)
                        continue;
                    
                    if (monitor.STATUS == MonitorPanel.Monitor.MSTATUS.UNMONITOR || monitor.PatientID == -1)
                        continue;

                    OleDbConnection conn = null;
                    try
                    {
                        conn = this.ConnInit();
                        string sql = "";
                        //查询CurrentInfo中是否有PatientID的记录
                        sql = @"SELECT COUNT(*) FROM [CurrentInfo] WHERE PatientID=" + monitor.PatientID;
                        OleDbCommand cmd = new OleDbCommand(sql, conn);
                        OleDbDataReader rdr = cmd.ExecuteReader();

                        if (rdr.Read())
                        {
                            if ((int)rdr[0] > 0)        //已有PatientID的记录，更新
                            {
                                sql = @"UPDATE [CurrentInfo] SET CurrentPipeType=" + monitor.PipeType.ToString() + ",CurrentSpeed=" + monitor.Speed.ToString()
                                    + ",CurrentValueRemind=" + monitor.RemindValue.ToString() + ",CurrentTotalCP=" + monitor.TotalCP.ToString()
                                    + ",CurrentTotalTime=" + monitor.TotalTime.ToString() + ",CurrentWarning='" + monitor.WARNING.ToString() + "' WHERE PatientID=" + monitor.PatientID.ToString();
                                cmd = new OleDbCommand(sql, conn);
                                cmd.ExecuteNonQuery();
                            }
                            else                        //插入PatientID的记录
                            {
                                sql = @"INSERT INTO [CurrentInfo] (PatientID,CurrentPipeType,CurrentSpeed,CurrentValueRemind,CurrentTotalCP,CurrentTotalTime,CurrentWarning) VALUES("
                                    + monitor.PatientID.ToString() + "," + monitor.PipeType.ToString() + "," + monitor.Speed.ToString() + "," + monitor.RemindValue.ToString() + ","
                                    + monitor.TotalCP.ToString() + "," + monitor.TotalTime.ToString() + ",'" + monitor.WARNING.ToString() + "')";

                                cmd = new OleDbCommand(sql, conn);
                                cmd.ExecuteNonQuery();
                            }
                        }
                    }
                    catch (System.Exception ex)
                    {
                        MessageBox.Show("保存实时数据出错:" + ex.Message);
                    }
                    finally
                    {
                        if (conn != null)
                            conn.Close();
                    }

                }
            }
        }

        private void DataReceived(Object sender, SerialDataReceivedEventArgs e)
        {
            if (bComClosing) return;
            
            try
            {
                bComRecving = true;
#if SLEEPMODE                
                int stime = 0;
                this.Invoke((EventHandler)(delegate
                {
                    stime = this.trackBarSleepTime.Value;
                }));
                
                Thread.Sleep(stime);

                //Thread.Sleep(40);
#endif
                int n = comm.BytesToRead;
                byte[] buf = new byte[n];

                comm.Read(buf, 0, buf.Length);

                for (int i = 0; i < buf.Length; ++i)
                {
                    strLogRcvData.Append(buf[i].ToString("X2") + " ");
                }
                strLogRcvData.Append("\r\n");
#if SLEEPMODE
                ParseData(buf);
#else
                //入队列                
                strLogQueue.Append("\r\nQueue.Count=" + qdata_recv.Count.ToString() + "\r\n");
                strLogQueue.Append("Enqueue:");
                for (int i = 0; i < buf.Length; ++i)
                {
                    qdata_recv.Enqueue(buf[i]);
                    strLogQueue.Append(buf[i].ToString("X2") + " ");
                }
                strLogQueue.Append("\r\n");
#endif
                //显示接受的数据
                strBuilder.Remove(0, strBuilder.Length);    //清空strbuilder
                this.Invoke((EventHandler)(delegate
                {
                    foreach (byte b in buf)
                    {
                        strBuilder.Append(b.ToString("X2") + " ");
                    }
                    this.textBoxRecv.AppendText(strBuilder.ToString());                    
                }));

                sendEvent.Set();
                //System.Threading.Monitor.Enter(tempLock);                
            }
            catch (System.Exception ex)
            {
                MessageBox.Show("DataReceived:" + ex.Message.ToString());
            }
            finally
            {
                bComRecving = false;
                //System.Threading.Monitor.Exit(tempLock);
            }
        }

        private void GetDataFromQueue()
        {
            for (int k = 0; k < 20; ++k)
            {
                qdata_recv.Enqueue(0x00);
            }
            while (true)
            {
                Thread.Sleep(1);
                if (qdata_recv.Count > 34)
                {
                    while (qdata_recv.Count > 0 && qdata_recv.Peek() != 0x5A) qdata_recv.Dequeue();

                    if (qdata_recv.Count > 16 && qdata_recv.Dequeue() == 0x5A)
                    {
                        byte len = qdata_recv.Dequeue();
                        if (qdata_recv.Count >= len - 1)
                        {
                            byte[] qbuf = new byte[len + 1];
                            qbuf[0] = 0x5A;
                            qbuf[1] = len;

                            int i = 0;
                            for (i = 0; i < len - 1; ++i)
                            {
                                qbuf[i + 2] = qdata_recv.Dequeue();
                            }
                            ParseData(qbuf);
                        }
                        //else Thread.Sleep(1);
                    }
                }
                else Thread.Sleep(3);
            }
        }

        private bool ParseData(byte[] buf)
        {
            strLogBuf.Append("\r\n-----Enter ParseData-----\r\n");
            List<byte> buffer = new List<byte>(1024);   //限制缓存大小
            
            buffer.AddRange(buf);
            strLogBuf.Append("List buffer:");
            foreach (byte b in buffer)
            {
                strLogBuf.Append(b.ToString("X2") + " ");
            }

            if (buffer.Count >= 6)
            {
                strLogBuf.Append("\r\nbuffer.Count=" + buffer.Count.ToString() + "\r\n");

                if (buffer[0] == 0x5A)              //检查同步字符
                {
                    byte len = buffer[1];            //获取命令长度
                    byte addr = buffer[2];           //获取发送端地址
                    byte cmd = buffer[3];

                    strLogBuf.Append("len=" + len.ToString() + " addr=" + addr.ToString() + " cmd=" + cmd.ToString());

                    int sum = 0;

                    //计算校验和
                    for (int i = 1; i < buffer.Count - 1; ++i)
                    {
                        sum += buffer[i];
                    }
                    byte checksum = (byte)sum;
                    strLogBuf.Append(" checksum=" + checksum.ToString() + "\r\n");
                    //判断校验和是否正确
                    if (checksum != buffer[buffer.Count - 1])
                    {
                        strLogBuf.Append("Checksum Error.\r\n");
                        strLogBuf.Append("Leave ParseData\r\n");
                        return false;
                    }

                    int monitorid = addr_id[addr];
                    strLogBuf.Append("monitorid=" + monitorid.ToString() + "\r\n");
                    if (monitorid != -1)
                    {
                        MonitorPanel.Monitor monitor = monitorPanel1.Monitors[monitorid];
                        strLogBuf.Append("ErrorCount=" + monitor.ErrorCount.ToString() + "\r\n");
                        monitor.ErrorCount = 0;
                        strLogBuf.Append("bNeedtoConfig:" + monitor.bNeedtoConfig.ToString() + "\r\n");
                        //设置指令
                        if (cmd < 0x72)
                        {
                            strLogBuf.Append("The command is one setting.\r\n");
                            if (buffer[4] != 0x00)
                            {
                                strLogBuf.Append("Return value error\r\n");
                                strLogBuf.Append("Leave ParseData\r\n");
                                return false;
                            }
                            else monitor.WARNING = MonitorPanel.Monitor.MWARNING.NORMAL;
                        }
                        else if (cmd == 0x76)
                        {
                            strLogBuf.Append("The command is all setting\r\n");
                            if (buffer[4] != 0x00)
                            {
                                strLogBuf.Append("Return value error\r\n");
                                strLogBuf.Append("Leave ParseData\r\n");
                                return false;
                            }
                            else
                            {
                                monitor.bNeedtoConfig = false;
                                monitor.WARNING = MonitorPanel.Monitor.MWARNING.NORMAL;
                            }
                        }
                        //获取状态指令，解析状态
                        else
                        {
                            strLogBuf.Append("The command is getting status\r\n");
                            int n = buffer.Count - 5;     //状态的长度:12 
                            List<Byte> lbuf = buffer.GetRange(4, n);
                            byte[] tempbuf = lbuf.ToArray();
                            MONITORSTATUS _tempstatus = (MONITORSTATUS)BytesToStuct(tempbuf, typeof(MONITORSTATUS));
                            if (!monitor.bNeedtoConfig)
                            {
                                monitor.Speed = _tempstatus.speed;
                                monitor.PipeType = _tempstatus.pipetype;
                                monitor.RemindTime = _tempstatus.remindtime;
                                monitor.RemindValue = _tempstatus.remindvalue;
                                monitor.TotalCP = _tempstatus.capacity;
                                monitor.TotalTime = _tempstatus.timeellapsed;
                                monitor.WARNING = (MonitorPanel.Monitor.MWARNING)_tempstatus.warning;
                            }
                            if (monitor.IsConfiging)
                            {
                                //把当前状态保存到临时Monitor变量，用于在配置对话框中显示实时信息
                                MonitorArgs monitorArgs = new MonitorArgs();
                                monitorArgs.MonitorID = monitor.MonitorID;
                                monitorArgs.Speed = _tempstatus.speed;
                                monitorArgs.PipeType = _tempstatus.pipetype;
                                monitorArgs.RemindValue = _tempstatus.remindvalue;
                                monitorArgs.RemindTime = _tempstatus.remindtime;
                                monitorArgs.Capacity = _tempstatus.capacity;
                                monitorArgs.TimeEllapsed = _tempstatus.timeellapsed;
                                monitorArgs.Warning = _tempstatus.warning;
                                
                                OnMonitorDataChanged(this, monitorArgs);
                            }

                            /*
                            if (monitor.drawMonitor())
                            {
                                monitorPanel1.updatePanel();
                            }
                            */
                            strLogBuf.Append("Warning:" + monitor.WARNING.ToString() + "\r\n");
                        }
                        
                        strLogBuf.Append("Set Count of " + monitor.MonitorAddr.ToString() + " to zero\r\n");
                    }
                    else
                    {
                        strLogBuf.Append("Leave ParseData\r\n");
                        return false;
                    }
                }
                else
                {
                    strLogBuf.Append("Leave ParseData\r\n");
                    return false;
                }
            }
            else
            {
                strLogBuf.Append("Leave ParseData\r\n");
                return false;
            }
            strLogBuf.Append("Leave ParseData\r\n");
            return true;
        }

        private void GetMonitorStatusThread()
        {
            sendEvent.Set();
            while (!bComClosing)
            {
                for (int i = 0; i < monitorPanel1.Monitors.Count; ++i)
                {
                    //System.Threading.Monitor.Enter(tempLock);
                    //Thread.Sleep(10);
                    try
                    {
                        MonitorPanel.Monitor monitor = monitorPanel1.Monitors[i];

                        if (!comm.IsOpen)
                        {
                            if (monitor.STATUS != MonitorPanel.Monitor.MSTATUS.UNMONITOR)
                            {
                                monitor.WARNING = MonitorPanel.Monitor.MWARNING.DISCONNECT;
                                //monitor.RefreshRec();                                
                            }
                            this.Invoke((EventHandler)(delegate
                            {
                                this.StatusPortStatusLabel.Text = "关闭";
                            }));
                            
                            Thread.Sleep(50);
                            continue;
                        }

                        else if (monitor.STATUS != MonitorPanel.Monitor.MSTATUS.UNMONITOR && !bComClosing)
                        {
                            bComSending = true;

                            monitor.ErrorCount++;
                            strLogCount.Append("\r\nMonitorID:" + monitor.MonitorID.ToString() + " ErrorCount=" + monitor.ErrorCount);
                            sendEvent.WaitOne(WAITTIMEOUT, false);
                            byte[] cmd;
                            if (monitor.bNeedtoConfig)
                            {
                                MONITORPARAMETERS mp = new MONITORPARAMETERS();
                                //mp.address = monitor.MonitorAddr;
                                mp.pipetype = monitor.PipeType;
                                mp.speed = monitor.Speed;
                                mp.remindtime = monitor.RemindTime;
                                mp.remindvalue = monitor.RemindValue;
                                mp.capacity = monitor.TotalCP;
                                mp.totaltime = monitor.TotalTime;
                                byte[] value = StructToBytes(mp);

                                cmd = CreateCommandString(monitor.MonitorAddr, FICOMMAND.SETALL, value);
                            }
                            else
                            {
                                //生成查询监控器运行参数命令
                                byte[] value = new byte[0];
                                cmd = CreateCommandString(monitor.MonitorAddr, FICOMMAND.GETSTATUS, value);
                            }
                            //显示发送的数据
                            strBuilder.Remove(0, strBuilder.Length);    //清空strbuilder
                            this.Invoke((EventHandler)(delegate
                            {
                                foreach (byte b in cmd)
                                {
                                    strBuilder.Append(b.ToString("X2") + " ");
                                }
                                this.textBoxSend.AppendText(strBuilder.ToString());
                            }));

                            comm.Write(cmd, 0, cmd.Length);

                            if (monitor.bNeedtoConfig)
                            {
                                monitor.ConfigCount--;
                                Thread.Sleep(3000);
                            }
                        }
                        else Thread.Sleep(10);
                    }
                    catch (System.Exception ex)
                    {
                        comm.Close();
                    }
                    finally
                    {                        
                        //System.Threading.Monitor.Exit(tempLock);
                        bComSending = false;
                    }
                }
            }
            //MessageBox.Show("出现错误，请重启应用程序");
        }

        public void CloseComm()
        {
            if (comm != null && comm.IsOpen)
            {
                bComClosing = true;                
                while (bComSending || bComRecving)
                {                    
                    Application.DoEvents();
                }
                comm.Close();
                bComClosing = false;
            }
        }

        //强制关闭串口，调用前GetMonitorStatusThread线程要暂停
        public void CloseCommForce()
        {
            this.bComSending = false;

            this.CloseComm();

            this.bComSending = true;
        }

        public void OpenComm()
        {
            SerialInit();
            
            if (!comm.IsOpen)
            {
                try
                {
                    comm.Open();
                    if (comm.IsOpen)
                    {
                        this.StatusPortStatusLabel.Text = "已开启";
                    }
                    else
                    {
                        MessageBox.Show("串口打开失败");
                        this.StatusPortStatusLabel.Text = "关闭";
                    }
                }
                catch (System.Exception ex)
                {
                    MessageBox.Show("串口:" + comm.PortName + " 打开失败");
                    this.StatusPortStatusLabel.Text = "关闭";
                    SerialInit();
                }
            }
        }

        private byte[] CreateCommandString(byte monitoraddr, FICOMMAND cmd, byte[] value)
        {
            List<byte> buf = new List<byte>();
            buf.Add(0x5A);      //第一个字节是同步字符
            buf.Add(0x00);      //第二个字节是长度，到后面再修改
            buf.Add(monitoraddr);
            buf.Add((byte)cmd); //添加命令类型

            for (int i = 0; i < value.Length; ++i)
            {
                buf.Add(value[i]);      //添加数据包
            }
            buf[1] = (byte)(value.Length + 4);  //修改长度

            byte checksum = 0;
            int sum = 0;
            for (int i = 1; i < buf.Count; ++i)
            {
                sum += buf[i];         //计算校验和
            }

            checksum = (byte)sum;

            buf.Add(checksum);
            return buf.ToArray();
        }

        private void 开启报警ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (this.bVoiceWarning)
            {
                this.开启报警ToolStripMenuItem.Text = "打开";
                bVoiceWarning = false;
            }
            else
            {
                this.开启报警ToolStripMenuItem.Text = "关闭";
                bVoiceWarning = true;
            }
        }

        public void UpdateListView(object sender, EventArgs e)
        {
            this.Invoke((EventHandler)(delegate
            {
                this.WarningListView.Items.Clear();
                foreach (MonitorPanel.Monitor monitor in monitorPanel1.Monitors)
                {
                    if (monitor.STATUS == MonitorPanel.Monitor.MSTATUS.ALARM)
                    {
                        ListViewItem row = new ListViewItem();
                        row.SubItems[0].Text = monitor.RoomID.ToString();
                        row.SubItems.Add(monitor.BedID.ToString());
                        row.SubItems.Add(monitor.WARNING.ToString());
                        this.WarningListView.Items.Add(row);
                    }
                }
            }));
        }

        public void ClearListView()
        {
            this.Invoke((EventHandler)(delegate
            {
                this.WarningListView.Clear();
            }));
        }

        public void UpdateListView(MonitorPanel.Monitor wm)
        {
            if (WarningMonitors.IndexOf(wm) == -1)  //List中无此记录
            {
                if (wm.WARNING != MonitorPanel.Monitor.MWARNING.NORMAL && wm.STATUS != MonitorPanel.Monitor.MSTATUS.UNMONITOR)
                {
                    WarningMonitors.Add(wm);
                    //this.PlayWarningSound(wm.WARNING);
                }
            }
            //若有此Monitor的告警记录
            else
            {
                if (wm.WARNING == MonitorPanel.Monitor.MWARNING.NORMAL || wm.STATUS == MonitorPanel.Monitor.MSTATUS.UNMONITOR) //此Monitor的告警转变为正常或状态为UMMONITOR则移除
                {
                    WarningMonitors.Remove(wm);
                }
            }
            this.Invoke((EventHandler)(delegate
            {
                this.WarningListView.Items.Clear();
                foreach (MonitorPanel.Monitor monitor in WarningMonitors)
                {
                    ListViewItem row = new ListViewItem();
                    row.SubItems[0].Text = monitor.RoomID.ToString();
                    row.SubItems.Add(monitor.BedID.ToString());
                    switch (monitor.WARNING)
                    {
                        case MonitorPanel.Monitor.MWARNING.FINISH:
                            row.SubItems.Add("输液已完成");
                            break;
                        case MonitorPanel.Monitor.MWARNING.LOWBATTERY:
                            row.SubItems.Add("电池欠压");
                            break;
                        case MonitorPanel.Monitor.MWARNING.BLOCK:
                            row.SubItems.Add("输液管阻塞");
                            break;
                        case MonitorPanel.Monitor.MWARNING.VALUEREMIND:
                            row.SubItems.Add("已达到提醒量");
                            break;
                        case MonitorPanel.Monitor.MWARNING.DISCONNECT:
                            row.SubItems.Add("通信中断");
                            break;
                        case MonitorPanel.Monitor.MWARNING.DIDOU:
                            row.SubItems.Add("滴斗水雾过多");
                            break;
                        case MonitorPanel.Monitor.MWARNING.FLUIDDEFICIENT:
                            row.SubItems.Add("亏液");
                            break;
                        default:
                            break;
                    }
                    this.WarningListView.Items.Add(row);
                }
            }));
        }

        private List<MonitorPanel.Monitor> WarningMonitors = new List<MonitorPanel.Monitor>();

        private SoundPlayer warningSound;

        private void PlayWarningSound(MonitorPanel.Monitor.MWARNING warning)
        {
            if (!bVoiceWarning)
                return;

            warningSound = new SoundPlayer();
            switch (warning)
            {
                case MonitorPanel.Monitor.MWARNING.BLOCK:
                    warningSound.SoundLocation = (string)this.soundFiles["BLOCK"];
                    break;
                case MonitorPanel.Monitor.MWARNING.DIDOU:
                    warningSound.SoundLocation = (string)this.soundFiles["DIDOU"];
                    break;
                case MonitorPanel.Monitor.MWARNING.DISCONNECT:
                    warningSound.SoundLocation = (string)this.soundFiles["DISCONNECT"];
                    break;
                case MonitorPanel.Monitor.MWARNING.FINISH:
                    warningSound.SoundLocation = (string)this.soundFiles["FINISH"];
                    break;
                case MonitorPanel.Monitor.MWARNING.FLUIDDEFICIENT:
                    warningSound.SoundLocation = (string)this.soundFiles["FLUIDDEFICIENT"];
                    break;
                case MonitorPanel.Monitor.MWARNING.LOWBATTERY:
                    warningSound.SoundLocation = (string)this.soundFiles["LOWBATTERY"];
                    break;
                case MonitorPanel.Monitor.MWARNING.VALUEREMIND:
                    warningSound.SoundLocation = (string)this.soundFiles["VALUEREMIND"];
                    break;
                default:
                    break;
            }
            try
            {
                warningSound.Load();
                warningSound.PlaySync();
            }
            catch
            {
                //MessageBox.Show(ex.Message.ToString());
            }
        }

        private void PlayWarningSoundCycleThread()
        {
            while (true)
            {
                Thread.Sleep(500);
                for (int i = 0; i < WarningMonitors.Count; ++i)
                {
                    Thread.Sleep(500);
                    try
                    {
                        if (WarningMonitors.Count > i && WarningMonitors[i] != null)
                        {
                            MonitorPanel.Monitor wm = WarningMonitors[i];
                            this.PlayWarningSound(wm.WARNING);
                        }
                    }
                    catch
                    {
                        continue;
                    }
                }
            }
        }

        public void WhenFIMStatusChanged()
        {
            if (OnFIMStatusChanged != null)
            {
                OnFIMStatusChanged(this, null);
            }
        }

        public static string ToHexString(byte[] bytes) // 0xae00cf => "AE00CF "
        {
            string hexString = string.Empty;
            if (bytes != null)
            {
                StringBuilder strB = new StringBuilder();

                for (int i = 0; i < bytes.Length; i++)
                {
                    strB.Append(bytes[i].ToString("X2"));
                }
                hexString = strB.ToString();
            }
            return hexString;
        }


        /// 结构体转byte数组
        /// </summary>
        /// <param name="structObj">要转换的结构体</param>
        /// <returns>转换后的byte数组</returns>
        public static byte[] StructToBytes(object structObj)
        {
            //得到结构体的大小
            int size = Marshal.SizeOf(structObj);
            //创建byte数组
            byte[] bytes = new byte[size];
            //分配结构体大小的内存空间
            IntPtr structPtr = Marshal.AllocHGlobal(size);
            //将结构体拷到分配好的内存空间
            Marshal.StructureToPtr(structObj, structPtr, false);
            //从内存空间拷到byte数组
            Marshal.Copy(structPtr, bytes, 0, size);
            //释放内存空间
            Marshal.FreeHGlobal(structPtr);
            //返回byte数组
            return bytes;
        }

        /// <summary>
        /// byte数组转结构体
        /// </summary>
        /// <param name="bytes">byte数组</param>
        /// <param name="type">结构体类型</param>
        /// <returns>转换后的结构体</returns>
        public static object BytesToStuct(byte[] bytes, Type type)
        {
            //得到结构体的大小
            int size = Marshal.SizeOf(type);
            //byte数组长度小于结构体的大小
            if (size > bytes.Length)
            {
                //返回空
                return null;
            }
            //分配结构体大小的内存空间
            IntPtr structPtr = Marshal.AllocHGlobal(size);
            //将byte数组拷到分配好的内存空间
            Marshal.Copy(bytes, 0, structPtr, size);
            //将内存空间转换为目标结构体
            object obj = Marshal.PtrToStructure(structPtr, type);
            //释放内存空间
            Marshal.FreeHGlobal(structPtr);
            //返回结构体
            return obj;
        }

        public enum FICOMMAND
        {
            SETADDRESS = 0x64,
            SETPIPETYPE = 0x66,
            SETSPEED = 0x68,
            SETREMINDVALUE = 0x6A,
            SETREMINDTIME = 0x6C,
            SETTOTALCP = 0x6E,
            SETTIMEPERBOTTLE = 0x70,
            GETCONFIG = 0x72,
            GETSTATUS = 0x74,
            SETALL = 0x76
        }

        [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
        public struct MONITORSTATUS
        {
            public byte pipetype;
            public byte speed;
            public Int16 remindvalue;
            public Int16 remindtime;
            public Int16 capacity;
            public Int16 timeellapsed;
            public byte powerstatus;
            public byte warning;
        }

        [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
        public struct MONITORPARAMETERS
        {
            //public byte address;
            public byte pipetype;
            public byte speed;
            public Int16 remindvalue;
            public Int16 remindtime;
            public Int16 capacity;
            public Int16 totaltime;
        }
        private void FIMForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            foreach (MonitorPanel.Monitor monitor in monitorPanel1.Monitors)
            {
                if (monitor.STATUS != MonitorPanel.Monitor.MSTATUS.UNMONITOR)
                {
                    MessageBox.Show("有患者正在输液，请先停止");
                    e.Cancel = true;
                    return;
                }
            }
            string strLogFile = strFilePath + "\\parse.log";
            using(FileStream fs = new FileStream(strLogFile, FileMode.Create))
            {
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(fs, strLogBuf.ToString());
            }
            strLogFile = strFilePath + "\\queue.log";
            using (FileStream fs = new FileStream(strLogFile, FileMode.Create))
            {
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(fs, strLogQueue.ToString());
            }
            strLogFile = strFilePath + "\\recvdata.log";
            using (FileStream fs = new FileStream(strLogFile, FileMode.Create))
            {
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(fs, strLogRcvData.ToString());
            }
            strLogFile = strFilePath + "\\errorcount.log";
            using (FileStream fs = new FileStream(strLogFile, FileMode.Create))
            {
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(fs, strLogCount.ToString());
            }
            this.CloseComm();
            if (_sthread != null)
            {
                _sthread.Abort();
            }

            if (_savethread != null)
            {
                _savethread.Abort();
            }

            if (_qthread != null)
            {
                _qthread.Abort();
            }

            if (_pthread != null)
            {
                _pthread.Abort();
            }
            
            monitorPanel1.Dispose();
            if (login_frm != null)
            {
                login_frm.Close();
            }
        }

        private void 语音报警配置ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SndCfgFrm scf = new SndCfgFrm(this);
            if (scf.ShowDialog() == DialogResult.OK)
            {
                cfg.WriteString("SOUNDFILES", "FINISH", (string)soundFiles["FINISH"]);
                cfg.WriteString("SOUNDFILES", "LOWBATTERY", (string)soundFiles["LOWBATTERY"]);
                cfg.WriteString("SOUNDFILES", "DISCONNECT", (string)soundFiles["DISCONNECT"]);
                cfg.WriteString("SOUNDFILES", "DIDOU", (string)soundFiles["DIDOU"]);
                cfg.WriteString("SOUNDFILES", "TIMEREMIND", (string)soundFiles["TIMEREMIND"]);
                cfg.WriteString("SOUNDFILES", "VALUEREMIND", (string)soundFiles["VALUEREMIND"]);
                cfg.WriteString("SOUNDFILES", "BLOCK", (string)soundFiles["BLOCK"]);
                cfg.WriteString("SOUNDFILES", "FLUIDDEFICIENT", (string)soundFiles["FLUIDDEFICIENT"]);
            }
        }

        private void 语音报警优先级设置ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            PriorityCfgFrm pcf = new PriorityCfgFrm();
            pcf.ShowDialog();
        }

        private void WarningListView_Click(object sender, EventArgs e)
        {
            int index = WarningListView.SelectedItems[0].Index;
            if (index < 0)
                return;
            int ID = WarningMonitors[index].MonitorID;
            monitorPanel1.SelectedMonitor(ID);
        }

        private void DebugItem_Click(object sender, EventArgs e)
        {
            if (this.DebugItem.Text == "打开")
            {
                this.splitContainer2.SplitterDistance = (int)(this.splitContainer2.Height * 0.5);
                this.splitContainer2.SplitterWidth = 1;
                this.splitContainer3.Visible = true;
                this.DebugItem.Text = "关闭";
            }
            else
            {
                this.splitContainer2.SplitterDistance = this.splitContainer2.Height + 2;
                this.splitContainer2.SplitterWidth = 1;
                this.splitContainer3.Visible = false;
                this.DebugItem.Text = "打开";
            }
        }

        private void 配置ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            PatientInfoFrm pifrm = new PatientInfoFrm(this);
            pifrm.ShowDialog();
        }

        private void trackBarSleepTime_Scroll(object sender, EventArgs e)
        {
            this.labelSleepTime.Text = trackBarSleepTime.Value.ToString();
        }

        private void 用户管理ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (this.bAdminLogin)
            {
                UserCtrlFrm ucfrm = new UserCtrlFrm(this);
                ucfrm.ShowDialog();
            }
            else
            {
                UserModifyPwdFrm umpfrm = new UserModifyPwdFrm(this);
                umpfrm.ShowDialog();
            }
        }

        private void 系统使用ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            System.Diagnostics.Process.Start(Application.StartupPath + "\\FIMonitor.chm");
        }

        private void 关于ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AboutFrm aboutfrm = new AboutFrm();
            aboutfrm.Show();
        }

        private void 退出ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            FIMForm.ActiveForm.Close();
        }

        private void splitContainer1_SplitterMoved(object sender, SplitterEventArgs e)
        {
            //monitorPanel1.Width = splitContainer1.Panel1.Width - splitContainer1.Panel1.AutoScrollMargin.Width;
            //monitorPanel1.Height = (int)(splitContainer1.Panel1.Height * 1.5);

            //MessageBox.Show("monitorPanel1.Width=:" + monitorPanel1.Width.ToString() + " drawPanel.Width=" + monitorPanel1.DrawPanel.Width.ToString());
        }


    }

    public class MonitorArgs : System.EventArgs
    {
        /*
        private MonitorPanel.Monitor _tempmonitor = new MonitorPanel.Monitor();

        public MonitorPanel.Monitor TempMonitor
        {
            get { return _tempmonitor; }
            set { _tempmonitor = value; }
        }*/
        
        private int _monitorid = 0;
        private byte _pipetype = 0;
        private byte _speed = 0;
        private Int16 _remindvalue = 0;
        private Int16 _remindtime = 0;
        private Int16 _capacity = 0;
        private Int16 _timeellapsed = 0;
        private byte _powerstatus = 0;
        private byte _warning = 0;

        public int MonitorID
        {
            get { return _monitorid; }
            set { _monitorid = value; }
        }

        public byte PipeType
        {
            get { return _pipetype; }
            set { _pipetype = value; }
        }

        public byte Speed
        {
            get { return _speed; }
            set { _speed = value; }
        }
        public Int16 RemindValue
        {
            get { return _remindvalue; }
            set { _remindvalue = value; }
        }
        public Int16 RemindTime
        {
            get { return _remindtime; }
            set { _remindtime = value; }
        }
        public Int16 Capacity
        {
            get { return _capacity; }
            set { _capacity = value; }
        }
        public Int16 TimeEllapsed
        {
            get { return _timeellapsed; }
            set { _timeellapsed = value; }
        }
        public byte PowerStatus
        {
            get { return _powerstatus; }
            set { _powerstatus = value; }
        }
        public byte Warning
        {
            get { return _warning; }
            set { _warning = value; }
        }
    }
}
